<template>
  <div class="canvas-container border border-gray-300 rounded">
    <h2 class="text-xl font-bold mb-4">Canvas表格（支持合并单元格）</h2>
    <canvas ref="canvasRef" class="canvas-table"></canvas>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue';

// 定义表格数据
const data = [{ id: 1, name: '张三', age: 25, operation: '编辑' }];

// 表格配置
const config = {
  headers: ['索引', '名称', '年龄', '操作'],
  columnWidths: [70, 180, 200, 380],
  rowHeight: 40,
  headerBgColor: '#e0e0e0', // 灰色表头
  borderColor: '#000000',
  textColor: '#000000',
  fontSize: 14,
  padding: 10,
};

// 创建canvas引用
const canvasRef = ref(null);

// 分析数据，找出需要合并的单元格
const analyzeMergeData = (data) => {
  const mergeGroups = {};
  let totalRows = 0;

  // 按ID和名称分组
  data.forEach((item, index) => {
    const key = `${item.id}-${item.name}`;
    if (!mergeGroups[key]) {
      mergeGroups[key] = [];
    }
    mergeGroups[key].push(index);
  });

  // 计算合并后的总行数
  Object.values(mergeGroups).forEach((group) => {
    totalRows += group.length;
  });

  return { mergeGroups, totalRows };
};

// 绘制表头
const drawTableHeader = (ctx, config, tableWidth) => {
  // 绘制表头背景
  ctx.fillStyle = config.headerBgColor;
  ctx.fillRect(
    0,
    0,
    config.columnWidths.reduce((sum, width) => sum + width, 0),
    config.rowHeight
  );

  // 绘制表头文字
  ctx.fillStyle = config.textColor;
  ctx.font = `${config.fontSize}px Arial`;
  ctx.textAlign = 'left';
  ctx.textBaseline = 'middle';

  let x = 0;
  config.headers.forEach((header, index) => {
    const columnWidth = config.columnWidths[index];
    ctx.fillText(header, x + config.padding, config.rowHeight / 2);
    x += columnWidth;
  });

  // 绘制表头边框
  ctx.strokeStyle = config.borderColor;
  ctx.lineWidth = 1;

  // 水平线
  ctx.beginPath();
  ctx.moveTo(0, 0);
  ctx.lineTo(tableWidth, 0);
  ctx.stroke();

  ctx.beginPath();
  ctx.moveTo(0, config.rowHeight);
  ctx.lineTo(tableWidth, config.rowHeight);
  ctx.stroke();

  // 垂直线
  let xPos = 0;
  config.columnWidths.forEach((width) => {
    ctx.beginPath();
    ctx.moveTo(xPos, 0);
    ctx.lineTo(xPos, config.rowHeight);
    ctx.stroke();
    xPos += width;
  });

  // 最右侧垂直线
  ctx.beginPath();
  ctx.moveTo(xPos, 0);
  ctx.lineTo(xPos, config.rowHeight);
  ctx.stroke();
};

// 绘制数据行
const drawTableRows = (ctx, data, mergeInfo, config, tableWidth) => {
  const { mergeGroups } = mergeInfo;
  const processedGroups = {};

  let currentY = config.rowHeight; // 从表头下方开始

  // 遍历数据
  data.forEach((row, rowIndex) => {
    const key = `${row.id}-${row.name}`;

    // 如果这个组已经处理过，跳过
    if (processedGroups[key]) return;

    // 标记这个组为已处理
    processedGroups[key] = true;

    // 获取这个组的所有行索引
    const groupRows = mergeGroups[key];
    const groupHeight = groupRows.length * config.rowHeight;

    // 绘制ID和名称（合并单元格）
    ctx.fillStyle = config.textColor;
    ctx.font = `${config.fontSize}px Arial`;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';

    // ID列
    ctx.fillText(
      row.id.toString(),
      config.columnWidths[0] / 2,
      currentY + groupHeight / 2
    );

    // 名称列
    ctx.fillText(
      row.name,
      config.columnWidths[0] + config.columnWidths[1] / 2,
      currentY + groupHeight / 2
    );

    // 绘制每个组内的年龄和操作（不合并）
    groupRows.forEach((dataIndex, i) => {
      const dataRow = data[dataIndex];
      const rowY = currentY + i * config.rowHeight;

      // 年龄列
      ctx.fillText(
        dataRow.age.toString(),
        config.columnWidths[0] +
          config.columnWidths[1] +
          config.columnWidths[2] / 2,
        rowY + config.rowHeight / 2
      );

      // 操作列
      ctx.fillText(
        dataRow.operation,
        config.columnWidths[0] +
          config.columnWidths[1] +
          config.columnWidths[2] +
          config.columnWidths[3] / 2,
        rowY + config.rowHeight / 2
      );
    });

    // 绘制边框
    ctx.strokeStyle = config.borderColor;
    ctx.lineWidth = 1;

    // 合并单元格的垂直边框
    // 左边框
    ctx.beginPath();
    ctx.moveTo(0, currentY);
    ctx.lineTo(0, currentY + groupHeight);
    ctx.stroke();

    // ID和名称的分隔线
    ctx.beginPath();
    ctx.moveTo(config.columnWidths[0], currentY);
    ctx.lineTo(config.columnWidths[0], currentY + groupHeight);
    ctx.stroke();

    // 名称和年龄的分隔线
    ctx.beginPath();
    ctx.moveTo(config.columnWidths[0] + config.columnWidths[1], currentY);
    ctx.lineTo(
      config.columnWidths[0] + config.columnWidths[1],
      currentY + groupHeight
    );
    ctx.stroke();

    // 年龄和操作的分隔线
    ctx.beginPath();
    ctx.moveTo(
      config.columnWidths[0] + config.columnWidths[1] + config.columnWidths[2],
      currentY
    );
    ctx.lineTo(
      config.columnWidths[0] + config.columnWidths[1] + config.columnWidths[2],
      currentY + groupHeight
    );
    ctx.stroke();

    // 右边框
    ctx.beginPath();
    ctx.moveTo(
      config.columnWidths.reduce((sum, width) => sum + width, 0),
      currentY
    );
    ctx.lineTo(
      config.columnWidths.reduce((sum, width) => sum + width, 0),
      currentY + groupHeight
    );
    ctx.stroke();

    // 年龄和操作列的水平分隔线
    groupRows.forEach((_, i) => {
      if (i > 0) {
        // 不是第一行
        const lineY = currentY + i * config.rowHeight;

        // 年龄列的水平分隔线
        ctx.beginPath();
        ctx.moveTo(config.columnWidths[0] + config.columnWidths[1], lineY);
        ctx.lineTo(
          config.columnWidths[0] +
            config.columnWidths[1] +
            config.columnWidths[2],
          lineY
        );
        ctx.stroke();

        // 操作列的水平分隔线
        ctx.beginPath();
        ctx.moveTo(
          config.columnWidths[0] +
            config.columnWidths[1] +
            config.columnWidths[2],
          lineY
        );
        ctx.lineTo(
          config.columnWidths.reduce((sum, width) => sum + width, 0),
          lineY
        );
        ctx.stroke();
      }
    });

    // 组的底部边框
    ctx.beginPath();
    ctx.moveTo(0, currentY + groupHeight);
    ctx.lineTo(
      config.columnWidths.reduce((sum, width) => sum + width, 0),
      currentY + groupHeight
    );
    ctx.stroke();

    // 更新Y坐标
    currentY += groupHeight;
  });
};

// 绘制表格
const drawTable = (ctx, data, mergeInfo, config, tableWidth, tableHeight) => {
  // 清空画布
  ctx.clearRect(0, 0, tableWidth, tableHeight);

  // 绘制表头
  drawTableHeader(ctx, config, tableWidth);

  // 绘制数据行
  drawTableRows(ctx, data, mergeInfo, config, tableWidth);
};

// 组件挂载后初始化canvas
onMounted(() => {
  const canvas = canvasRef.value;
  if (!canvas) return;

  const ctx = canvas.getContext('2d');
  if (!ctx) return;

  // 获取设备像素比
  const dpr = window.devicePixelRatio || 1;
  for (let i = 0; i < 500; ++i) {
    data.push({
      id: i + 1,
      name: `张三${i + 1}`,
      age: 25 + i,
      operation: '编辑',
    });
  }
  console.log(data);

  // 分析数据，找出需要合并的单元格
  const mergeInfo = analyzeMergeData(data);

  // 计算表格总宽度
  const tableWidth = config.columnWidths.reduce((sum, width) => sum + width, 0);

  // 计算表格总高度（考虑合并后的实际行数）
  const tableHeight = config.rowHeight * (mergeInfo.totalRows + 1); // +1 for header

  // 设置 canvas 元素的样式尺寸
  canvas.style.width = `${tableWidth}px`;
  canvas.style.height = `${tableHeight}px`;

  // 设置 canvas 的实际绘制尺寸
  canvas.width = tableWidth * dpr;
  canvas.height = tableHeight * dpr;

  // 缩放上下文以匹配设备像素比
  ctx.scale(dpr, dpr);

  // 绘制表格
  drawTable(ctx, data, mergeInfo, config, tableWidth, tableHeight);
});
</script>

<style scoped>
.canvas-container {
  padding: 20px;
  background-color: white;
}

.canvas-table {
  margin-top: 10px;
}
</style>
